package logic;
import java.io.*;
import java.lang.Math;

import dk.ange.octave.OctaveEngine;
import dk.ange.octave.OctaveEngineFactory;
import dk.ange.octave.type.OctaveComplex;
import dk.ange.octave.type.OctaveString;

public class GATE
{
	private boolean inLib=false;
	private String gate_name;
	private String gate_class=null;
	private double phase=0.0;
	private double z_rotation1;
	private double y_rotation;
	private double z_rotation2;
	private QVAR target_qbit;
	private QVAR[] control_qbits;
	
	public GATE(String g)
	{
		gate_name=g;
		gate_class=g;
		inLib=true;
	}
		
	public GATE(String g,double z1,double y,double z2)
	{
		gate_name=g;
		gate_class=g;
		z_rotation1=z1;
		z_rotation2=z2;
		y_rotation=y;
	}
	public GATE(String g,String c,double z1,double y,double z2)
	{
		gate_name=g;
		gate_class=c;
		z_rotation1=z1;
		z_rotation2=z2;
		y_rotation=y;
	}
	public GATE(String g,double z1,double y,double z2,double ph)
	{
		gate_name=g;
		gate_class=g;
		z_rotation1=z1;
		y_rotation=y;
		z_rotation2=z2;
		phase=ph;
	}
    public GATE(String g,String c,double z1,double y,double z2,double ph)
	{
		gate_name=g;
		gate_class=c;
		z_rotation1=z1;
		z_rotation2=z2;
		y_rotation=y;
		phase=ph;
	}

	public GATE copy()
	{
		return new GATE(gate_name,gate_class,z_rotation1,y_rotation,z_rotation2);
	}
	public void connectQubits(QVAR target, QVAR[] controls)
	{
		target_qbit=target;
		control_qbits=controls;
	}
	
	public String getName()
	{
		return gate_name;
	}
	public QVAR getTargetQbit()
	{
		return target_qbit;
	}	
	public boolean isSingleQubitGate()
	{
		return (control_qbits==null);
	}
	public void printDefinition()
	{
		if(gate_class!="NOT")
		 System.out.printf("DEFINE %s %1.3f %1.3f %1.3f\n",gate_class,z_rotation1,y_rotation,z_rotation2);
	}
	public void output()
	{
		if(control_qbits!=null)
		{
			System.out.print("CONTROL ");	 
			System.out.print(gate_class+" ");
			for(QVAR var:control_qbits)
			{
				System.out.print(var.getName()+",");
			}
			System.out.println(" "+target_qbit.getName());
		}
		else
		{
			if(gate_class=="PHASE")
			{
				System.out.println("PHASE"+" "+phase+" "+target_qbit.getName());
				return;
			}	
			System.out.print(gate_class+" ");
			System.out.println(target_qbit.getName());
		}
	}

	public GATE[] one_control_decompose()
	{
		if(control_qbits.length!=1)
			return null;
		String[] names=new String[5];
		for(int i=0;i<5;i++)
			names[i]=gate_name+"_decompose_"+i;
				
		int k=0;	 
		if(phase!=0)
		 k=1;	
		GATE[] gates=new GATE[5+k];
		if(k==1)
         {
			 gates[0]=new GATE("PHASE","PHASE",0,0,0,phase);
			 gates[0].connectQubits(control_qbits[0], null);
         }
		gates[0+k]=new GATE(names[0],z_rotation1,y_rotation/2,0);
		gates[0+k].connectQubits(target_qbit, null);
		gates[1+k]=new GATE(names[1],"NOT",Math.PI,Math.PI,0);
		gates[1+k].connectQubits(target_qbit, control_qbits);
		gates[2+k]=new GATE(names[2],0,-y_rotation/2,-(z_rotation1+z_rotation2)*0.5);
		gates[2+k].connectQubits(target_qbit, null);
		gates[3+k]=new GATE(names[3],"NOT",Math.PI,Math.PI,0);
		gates[3+k].connectQubits(target_qbit, control_qbits);
		gates[4+k]=new GATE(names[4],(z_rotation2/2+z_rotation1/-2),0,0);
		gates[4+k].connectQubits(target_qbit, null);

		return gates;
	}
	
	public GATE[] two_control_decompose()
	{
	 GATE V=this.squareRootGate();
	 GATE Vt=V.inverseGate();
	 GATE V2=V.copy();
	 GATE cnot1=new GATE("NOT","NOT",Math.PI,Math.PI,0);
	 GATE cnot2=new GATE("NOT","NOT",Math.PI,Math.PI,0);
	 
	 QVAR[] control=new QVAR[1], control2=new QVAR[1];
	 control[0]=control_qbits[0];
	 control2[0]=control_qbits[1] ;
	 V.connectQubits(target_qbit, control);
	 Vt.connectQubits(target_qbit, control);
	 V2.connectQubits(target_qbit, control2);
	 cnot1.connectQubits(control_qbits[1], control);
	 cnot2.connectQubits(control_qbits[1], control);
	 
	 GATE[] decomposes1=V.one_control_decompose();
	 GATE[] decomposes2=Vt.one_control_decompose();
	 GATE[] ret=new GATE[2+decomposes2.length+decomposes1.length*2];;
	 
	 int i=0;
	 for(GATE g:decomposes1)
	  ret[i++]=g;
	 ret[i++]=cnot1;
	 for(GATE g:decomposes2)
      ret[i++]=g;
	 ret[i++]=cnot2;
	 for(GATE g:decomposes1)
	  ret[i++]=g; 
	 return ret;
	}
	
	private GATE inverseGate()
	{
		return new GATE(gate_name+"_inverse",gate_class+"_inverse",-z_rotation2,-y_rotation,-z_rotation1);
	}
	
	public GATE squareRootGate()
	{
		double cos_delta,nx,ny,nz,sqnz,sqny,sqnx,delta,new_delta;
		double z_rot1,z_rot2;
		double[] y_rot=new double[2];
		
		final OctaveEngine octave = new OctaveEngineFactory().getScriptEngine();
        String query="mat=yz_rotation_sqrt("+z_rotation1+","+y_rotation+","+z_rotation2+");";
        octave.eval(query);
        final OctaveComplex b = octave.get(OctaveComplex.class, "mat");
		GATE sqr=new GATE(gate_name+"_squareRoot",b.getReal(1,1),b.getReal(1,2),b.getReal(1,3));
		octave.close();
		
		return sqr;

	}
	public String decompose1()
	{		
		final OctaveEngine octave = new OctaveEngineFactory().getScriptEngine();
        String query="seq=decompose1([ "+z_rotation1+" "+y_rotation+" "+z_rotation2+"]);";
        octave.eval(query);	
        final OctaveString seq=octave.get(OctaveString.class,"seq");
        String s=seq.getString();
        return s;
	}	
}
